#include "module.h"

/// @brief netlink socket
static struct sock *nlsk = NULL;

/// @brief 使用netlink向指定用户进程发送数据
/// @param pid 目标进程pid
/// @param data 
/// @param len 
/// @return 
int nlSend(unsigned int pid, void *data, unsigned int len) {
	int retval;
	struct nlmsghdr *nlh;
	struct sk_buff *skb;
	// init sk_buff
	skb = nlmsg_new(len, GFP_ATOMIC);
	if (skb == NULL) {
		printk(KERN_WARNING "[fw netlink] alloc reply nlmsg skb failed!\n");
		return -1;
	}
	// 添加nlmsg消息头
	nlh = nlmsg_put(skb, 0, 0, 0, NLMSG_SPACE(len) - NLMSG_HDRLEN, 0);
	// send data
	// 设置nlmsg消息体
	memcpy(NLMSG_DATA(nlh), data, len);
    //NETLINK_CB(skb).portid = 0;
	NETLINK_CB(skb).dst_group = 0;
	// 单播发送到指定进程
	retval = netlink_unicast(nlsk, skb, pid, MSG_DONTWAIT);
	printk("[fw netlink] send to user pid=%d,len=%d,ret=%d\n", pid, nlh->nlmsg_len - NLMSG_SPACE(0), retval);
	return retval;
}

/// @brief 使用netlink接收数据
/// @param skb 
void nlRecv(struct sk_buff *skb) {
	void *data;
	struct nlmsghdr *nlh = NULL;
	unsigned int pid,len;
    // check skb
    nlh = nlmsg_hdr(skb);
	// 检查skb合法性
	if ((nlh->nlmsg_len < NLMSG_HDRLEN) || (skb->len < nlh->nlmsg_len)) {
		printk(KERN_WARNING "[fw netlink] Illegal netlink packet!\n");
		return;
	}
    // deal data
	data = NLMSG_DATA(nlh);
    pid = nlh->nlmsg_pid;
    len = nlh->nlmsg_len - NLMSG_SPACE(0);
	if(len<sizeof(struct APPRequest)) {
		printk(KERN_WARNING "[fw netlink] packet size < APPRequest!\n");
		return;
	}
	printk("[fw netlink] data receive from user: user_pid=%d, len=%d\n", pid, len);
	// 转至统一请求处理
	dealAppMessage(pid, data, len);
}

/// @brief netlink初始化参数
struct netlink_kernel_cfg nltest_cfg = {
	.groups = 0,		// 组ID 
	.flags = 0,			// 标志位
	.input = nlRecv,	// 输入处理函数
	.cb_mutex = NULL,	// 回调函数互斥锁
	.bind = NULL,		// 绑定函数
	.unbind = NULL,		// 解绑函数
	.compare = NULL,	// 比较函数
};

/// @brief 初始化 netlink
struct sock *netlink_init() {
    nlsk = netlink_kernel_create(&init_net, NETLINK_MYFW, &nltest_cfg);
	if (!nlsk) {
		printk(KERN_WARNING "[fw netlink] can not create a netlink socket\n");
		return NULL;
	}
	printk("[fw netlink] netlink_kernel_create() success, nlsk = %p\n", nlsk);
    return nlsk;
}

/// @brief 释放 netlink
void netlink_release() {
    netlink_kernel_release(nlsk);
}